package PIServer;

import Codec.MessageCodecFactory;
import DataManager.DbManager;
import Utility.ThreadWarningSystem;
import com.sun.net.httpserver.HttpServer;

import java.io.*;
import java.lang.management.ThreadInfo;
import java.net.*;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Timer;

import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.filter.logging.LogLevel;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.SocketSessionConfig;

import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {

    public static int CLIENT_BUILD_REQUIRED = 9543;
    public static String CLIENT_VERSION_MOST_UPDATED = null;
    public static String client_version = null;
    public static String server_version = "1.1";
    public static String server_Build = "";
    public static String policyFile = "";
    public static int idleTimeBeforeDisconnect = 1200;
    public static int maximumConnections = 1000;
    public static Logger log = LoggerFactory.getLogger(Main.class.getName());
    public static int socketPort = 16800;
    private static int httpPort = 16801;
    private static NioSocketAcceptor acceptor = null;
    //time controller for items - created by Anlh
    private static Timer timerForNoticeAllItem = new Timer();
    public static Timer timerForResetLimit = new Timer();
    private static HttpServer httpServer = null;
    static String javaBin;
    static String jarPath;

    public static void main(String[] args) throws Exception {
        log.info("Starting Server...");
        parseCommandLines(args);
//        loadServerProperties();
        initThreadWarningSystem();
        javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
        jarPath = getJarPath();
        // get latest client version
//        ResetTimeTask.updateLastestClientVersion();
        try {
            ResourceBundle rbServerVersion = ResourceBundle.getBundle("build");
            Main.server_Build = Main.getResourceBundleProperty(rbServerVersion, "BUILD");
            log.info("Server Version: {} (Build {})",
                    server_version, server_Build);
//            log.info("Cross Domain Policy: " + policyFile);
            //log.info("Vivoo Mobile Server is allowed to manage accounts: " + manageAccounts);
            log.info("Idle time before disconnect: " + idleTimeBeforeDisconnect);
            log.info("DB Server: " + LocalSettings.DB_SERVER);
            log.info("Path: {}", jarPath);

            //checkIfAdminAccountExists();

//            setTimerTasks();
//            ResetTimeTask.checkEventDate();

        } catch (Exception ex) {
            log.error(ex.getClass().getName(), ex);
//            ex.printStackTrace();
            return;
        }

        initSocketBind();
//        initHttpBind();
//        AutomaticBotTienLen bot = new AutomaticBotTienLen("vivoo.vn",socketPort);
        DbManager.GetCommonDataManager();

    }

    public static final String getResourceBundleProperty(ResourceBundle rb, String propToken) {
        String msg = "";
        try {
            msg = rb.getString(propToken);
        } catch (MissingResourceException e) {
            log.error("Token {} not in property file!", propToken);
        }
        return msg;
    }

    public static void shutdownServer() {
        try {
            log.info("Vivoo Mobile Server has shutdown");

            log.info("Clean up users");
            VMSProtocolHandler.cleanUp();
            log.info("Stop socket services");
            acceptor.unbind();
            httpServer.stop(0);
            log.info("Shutdown connection pool");
            DbManager.shutdownConnectionPool();
            log.info("Exit system...");
            System.exit(0);
        } catch (Exception ex) {
            log.error(ex.getClass().getName(), ex);
        }
    }

    private static void addLogger(DefaultIoFilterChainBuilder chain) throws Exception {
        LoggingFilter logger = new LoggingFilter();
        logger.setMessageReceivedLogLevel(LogLevel.WARN);
        logger.setMessageSentLogLevel(LogLevel.WARN);
        logger.setSessionClosedLogLevel(LogLevel.WARN);
        logger.setSessionOpenedLogLevel(LogLevel.WARN);
        logger.setSessionIdleLogLevel(LogLevel.WARN);
        logger.setSessionCreatedLogLevel(LogLevel.WARN);

        chain.addLast("logger", logger);
    }

    private static boolean checkIfCustomLogicClassFileExists() {
        boolean exists = (new File("CustomLogic.jar")).exists();

        return exists;
    }

    private static void parseCommandLines(String[] args) {
        try {
            String tmpStr = "";

            for (int i = 0; i < args.length; i++) {
                if (args[i].contains("-db=")) {
                    tmpStr = args[i].replace("-db=", "").trim();
                    LocalSettings.DB_SERVER = tmpStr;
                }
                if (args[i].contains("-memcache=")) {
                    tmpStr = args[i].replace("-db=", "").trim();
                    LocalSettings.MEMCACHE_SERVER = tmpStr;
                }
                if (args[i].contains("-vcore=")) {
                    tmpStr = args[i].replace("-vcore=", "").trim();
                    LocalSettings.VCORE_URL = tmpStr;
                }
                if (args[i].contains("-cardpay=")) {
                    tmpStr = args[i].replace("-cardpay=", "").trim();
                    LocalSettings.CARDPAY_URL = tmpStr;
                }
                if (args[i].contains("-port=")) {
                    tmpStr = args[i].replace("-port=", "").trim();
                    socketPort = Integer.parseInt(tmpStr);
                }
                if (args[i].contains("-adminport=")) {
                    tmpStr = args[i].replace("-adminport=", "").trim();
                    httpPort = Integer.parseInt(tmpStr);
                }
                if (args[i].contains("-idletimeout=")) {
                    tmpStr = args[i].replace("-idletimeout=", "").trim();
                    idleTimeBeforeDisconnect = Integer.parseInt(tmpStr);
                }
            }
        } catch (Exception ex) {
            log.error(ex.getClass().getName(), ex);
            System.out.println("There was an error reading the command line arguments");
            return;
        }
    }

    private static void initSocketBind() {
        try {
            acceptor = new NioSocketAcceptor();
            //IoAcceptorConfig config = new SocketAcceptorConfig();
            //DefaultIoFilterChainBuilder chain = config.getFilterChain();
            DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();

//            ------
//            TextLineCodecFactory tlcf = new TextLineCodecFactory(Charset.defaultCharset(),LineDelimiter.WINDOWS,LineDelimiter.WINDOWS);
//            //TextLineCodecFactory tlcf = new TextLineCodecFactory(Charset.defaultCharset(), LineDelimiter.WINDOWS);
//            tlcf.setDecoderMaxLineLength(Integer.MAX_VALUE);
//            tlcf.setEncoderMaxLineLength(Integer.MAX_VALUE);
//
//            //chain.addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.defaultCharset(), LineDelimiter.NUL)));
//            chain.addLast("codec", new ProtocolCodecFilter(tlcf));
//            ------

            MessageCodecFactory msgcd = MessageCodecFactory.getInstance();
            chain.addLast("codec", new ProtocolCodecFilter(msgcd));

            // Use one thread pool for most events.
            chain.addLast("executor", new ExecutorFilter(
                    Runtime.getRuntime().availableProcessors() * 2,
                    Runtime.getRuntime().availableProcessors() * 8));
//            // and another dedicated thread pool for 'filterWrite' events.
//            chain.addLast("executorWrite", new ExecutorFilter(IoEventType.WRITE));

            addLogger(chain);

            SocketSessionConfig cfg = acceptor.getSessionConfig();
            cfg.setIdleTime(IdleStatus.BOTH_IDLE, Main.idleTimeBeforeDisconnect);
            cfg.setKeepAlive(true);

            acceptor.setReuseAddress(true);
            //httpAcceptor.bind(new InetSocketAddress(socketPort), new VMSProtocolHandler(), config);
            acceptor.setHandler(new VMSProtocolHandler());
            acceptor.bind(new InetSocketAddress(socketPort));
            log.info("Socket listening on port: " + socketPort);
        } catch (Exception ex) {
            if (acceptor != null) //httpAcceptor.unbindAll();
            {
                acceptor.unbind();
            }
            log.error("Cannot connect", ex);
        }
    }

//    private static void initHttpBind() {
//        try {
//            httpAcceptor = new NioSocketAcceptor();
//            //IoAcceptorConfig config = new SocketAcceptorConfig();
//            //DefaultIoFilterChainBuilder chain = config.getFilterChain();
//            DefaultIoFilterChainBuilder chain = httpAcceptor.getFilterChain();
//
////            ------
//            //TextLineCodecFactory tlcf = new TextLineCodecFactory(Charset.defaultCharset(),LineDelimiter.WINDOWS,LineDelimiter.WINDOWS);
//            TextLineCodecFactory tlcf = new TextLineCodecFactory( Charset.forName( "UTF-8" ));
////            httpAcceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(tlcf));
//            //TextLineCodecFactory tlcf = new TextLineCodecFactory(Charset.defaultCharset(), LineDelimiter.WINDOWS);
//            tlcf.setDecoderMaxLineLength(Integer.MAX_VALUE);
//            tlcf.setEncoderMaxLineLength(Integer.MAX_VALUE);
//
//            //chain.addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.defaultCharset(), LineDelimiter.NUL)));
//            chain.addLast("codec", new ProtocolCodecFilter(tlcf));
////            ------
//
////            MessageCodecFactory msgcd = new MessageCodecFactory();
////            chain.addLast("codec", new ProtocolCodecFilter(msgcd));
//
//            addLogger(chain);
//
//            httpAcceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, VMServer.idleTimeBeforeDisconnect);
//            httpAcceptor.setReuseAddress(true);
//            //httpAcceptor.bind(new InetSocketAddress(socketPort), new VMSProtocolHandler(), config);
//            httpAcceptor.setHandler(new AdminProtocolHandler());
//            httpAcceptor.bind(new InetSocketAddress(httpPort));
//            log.info("HTTP Listening on port: " + httpPort);
//        }
//        catch (Exception ex)
//        {
//            if(httpAcceptor != null)
//                //httpAcceptor.unbindAll();
//                httpAcceptor.unbind();
//            log.error("Cannot connect", ex);
//        }
//    }
//    private static void initHttpBind() {
//        try {
//            InetSocketAddress addr = new InetSocketAddress(httpPort);
//            httpServer = HttpServer.create(addr, 0);
//
//            httpServer.createContext("/", new AdminHttpHandler());
//            httpServer.setExecutor(Executors.newCachedThreadPool());
//            httpServer.start();
//            log.info("HTTP listening on port: " + httpPort);
//        } catch (Exception ex) {
//            log.error("Cannot bind HTTP", ex);
//        }
//    }
//    private static void setTimerTasks() {
//
//        try {
//            ServiceTimerTask noticeAllTasks = new ServiceTimerTask();
//            timerForNoticeAllItem.schedule(noticeAllTasks, 0, ItemService.PERIODICAL_TIME_CHECK_NOTICE_QUEUE * 1000);
//
//            long serverStartTime = System.currentTimeMillis() + ItemService.TIME_ZONE_PLUS;
//            long lastDistanceTime = (serverStartTime % ItemService.A_DAY_DURATION);
//            long nexTimeReset = (ItemService.A_DAY_DURATION - lastDistanceTime);
//            if (nexTimeReset > (ItemService.A_DAY_DURATION / 2)) {
//                nexTimeReset -= ItemService.A_DAY_DURATION / 2;
//            }
//            if (nexTimeReset > ItemService.TIME_TASK_PERIOD) {
//                do {
//                    nexTimeReset -= ItemService.TIME_TASK_PERIOD;
//                } while (nexTimeReset > ItemService.TIME_TASK_PERIOD);
//            }
//            long tmpTime = nexTimeReset / 1000;
//            long hour = tmpTime / (3600);
//            tmpTime -= hour * 3600;
//            long minute = tmpTime / (60);
//            tmpTime -= minute * 60;
//            long second = tmpTime;
//            // 2011/7/3 - Nhan - update string format 1:03:08 instead of 1:3:8
//            log.info(String.format("Time delay for Reset tasks start in %d:%02d:%02d",  hour, minute, second));
//            ResetTimeTask resetTask = new ResetTimeTask();
//
////            ArrayList<String> dailyLoginMoneyRec = new ArrayList<String>();
////            dailyLoginMoneyRec.add("5125");
////            Memcache.getInstance().set("LoginMoneyRec", 3600*24, dailyLoginMoneyRec);
//
//            timerForResetLimit.schedule(resetTask, nexTimeReset, ItemService.A_DAY_DURATION / 12);
//            //timerForResetLimit.schedule(resetTask, 60000, 60000);.
//            NewsService.getNewsList();
////            ResetTimeTask.updateBestPlayerList();
////            Memcache.getInstance().delete("bestPlayerList");
////            Memcache.getInstance().delete("mostWealthyPlayerList");
//        } catch (Exception ex) {
//            log.error("EXCEPTION in setTimerTasks", ex);
//        }
//
//    }
//    private static void loadServerProperties() {
//        try {
//            ServerProperties propertyReader = new ServerProperties();
//
//            policyFile = propertyReader.LoadCrossDomainPolicy();
//        } catch (Exception ex) {
//            log.error(ex.getClass().getName(), ex);
//            log.error("There was an error reading the server.xml file. "
//                    + "Vivoo Mobile Server will be shutdown");
//            return;
//        }
//
//    }
    private static String getJarPath() {
        String name = Main.class.getName().replace('.', '/');
        String s = Main.class.getResource("/" + name + ".class").toString();
//        s = s.replace('/', File.separatorChar);
        if (s.indexOf(".jar") == -1) {
            return null;
        }
        s = s.substring(0, s.indexOf(".jar") + 4);

        int i = s.indexOf("file:\\\\");
        if (i >= 0) {
            s = s.substring(i + 6);
        } else {
            // Unix
            i = s.indexOf("file:");
            s = s.substring(i + 5);
        }
        return s;
    }

//    public static void restartServer()
//    {
//        try {
//            /* is it a jar file? */
//            if ((jarPath==null) || (!jarPath.endsWith(".jar"))) {
//                return;
//            }
//
//            /* Build command: java -jar application.jar */
////            InetAddress ip = InetAddress.getLocalHost();
//            final ArrayList<String> command = new ArrayList<String>();
//            command.add(javaBin);
////            command.add("-Dcom.sun.management.jmxremote.port=6789");
////            command.add("-Dcom.sun.management.jmxremote.authenticate=false");
////            command.add("-Dcom.sun.management.jmxremote.ssl=false");
////            command.add("-Djava.rmi.server.hostname="
////                    + ip.getHostAddress());
//            command.add("-jar");
//            command.add(jarPath);
//
//            log.info("Restart parameters: {}", command);
//            final ProcessBuilder builder = new ProcessBuilder(command);
//            builder.redirectErrorStream(true);
//            builder.start();
//            log.info("Initialized new process");
//
//            shutdownServer();
//
//        } catch (Exception ex) {
//            log.error(ex.getClass().getName(), ex);
//        }
//    }
    private static void initThreadWarningSystem() {
        ThreadWarningSystem tws = new ThreadWarningSystem();
        tws.addListener(new ThreadWarningSystem.Listener() {

            public void deadlockDetected(ThreadInfo inf) {
                log.error(inf.toString());
            }

            public void thresholdExceeded(ThreadInfo[] threads) {
            }

            public void finishDeadlockScan() {
                Main.shutdownServer();
            }
        });
    }
}
